# -*- coding: utf-8 -*-
"""

    taggable.models
    ---------------

    Django models.

    :copyright: 2008 by Gustavo Picon
    :license: Apache License 2.0

"""

from django.db import models, transaction, connection
from taggable.managers import TaggedManager
from taggable import signals


class Tagged(models.Model):
    """ Tagged abstract class.
    """

    objects = TaggedManager()

    def _get_fields(self, relmodel, fields):
        cols, vals = [], []
        for field in fields:
            cols.append(relmodel._meta.get_field_by_name(field)[0].column)
            vals.append(self.__getattribute__(field).pk)
        return cols, vals

    def save(self, *args, **kwargs):
        """ Saves the tagged object and handles the stats table maintenance.
        """
        super(Tagged, self).save(*args, **kwargs)
        cursor = connection.cursor()

        # Why raw queries instead of using the django orm?
        #
        # Because the weakest point in the django orm, is UPDATE.
        #
        # UPDATE in the django orm suffers from 2 _serious_ problems:
        #
        # - it updates ALL the fields in a record, even when only 1 or 2 fields
        #   were modified
        # - it doesn't support setting a value that is relative to something
        #   else, like `count=count+1` or `lencol=CHARACTER_LENGTH(strcol)`
        #
        # So one way or the other, you're condemned to race condition HELL.

        for fields, relmodel in self.__class__.taggable_stats:
            table = relmodel._meta.db_table
            cols, vals = self._get_fields(relmodel, fields)
            colstr = ' AND '.join(['%s=%%s' % col for col in cols])
            sql = 'SELECT 1 FROM %s WHERE %s' % (table, colstr)
            cursor.execute(sql, vals)

            if cursor.fetchone():
                sql = 'UPDATE %s SET count=count+1 WHERE %s' % (table, colstr)
            else:
                cols.append('count')
                vals.append(1)
                sql = 'INSERT INTO %s (%s) VALUES (%s)' % (
                  table,
                  ', '.join(cols),
                  ', '.join(['%s' for i in vals])
                )
            cursor.execute(sql, vals)

        transaction.commit_unless_managed()


    def delete(self):
        """ Call the delete in the queryset to handle stats maintenance
        """
        self.__class__.objects.filter(pk=self.pk).delete()


    class Meta:
        """
        Abstract model.
        """
        abstract = True




#~
